package com.yungu.swift.api.base;

import com.yungu.swift.api.base.gateway.GatewayHeader;
import com.yungu.swift.api.route.v2.passenger.PasLoginApi;
import com.yungu.swift.api.utils.JwtHelper;
import com.yungu.swift.autoconfig.redis.RedisStringCacheService;
import com.yungu.swift.base.SpringContextHolder;
import com.yungu.swift.base.model.api.ApiParam;
import com.yungu.swift.constants.CommonConstant;
import com.yungu.swift.utils.JsonUtils;
import com.yungu.swift.utils.ReflectUtils;
import com.yungu.swift.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RequestBody;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Objects;

/**
 * @author : cuixiuyin
 * @date : 2019/6/13
 */
@Slf4j
@ControllerAdvice
public class RequestBodyAdvice implements org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice {

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return methodParameter.hasParameterAnnotation(RequestBody.class) &&
                ApiParam.class.isAssignableFrom(methodParameter.getParameterType());
    }

    @Override
    public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        if (o == null) {
            o = new ApiParam();
        }
        token2Body(o, httpInputMessage);
        return o;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        return httpInputMessage;
    }

    @Override
    public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        token2Body(o, httpInputMessage);
        return o;
    }


    /**
     * 解析 token 信息，将 token 转换成 request body
     *
     * @param o
     * @param httpInputMessage
     */
    private void token2Body(Object o, HttpInputMessage httpInputMessage) {
        HttpHeaders headers = httpInputMessage.getHeaders();
        if (headers.get(GatewayHeader.rid) == null || headers.get(GatewayHeader.appid) == null) {
            return;
        }
        String rid = headers.get(GatewayHeader.rid).get(0);
        String appid = headers.get(GatewayHeader.appid).get(0);
        String prefix = Objects.equals(rid, CommonConstant.PASSENGER + "") ? PasLoginApi.ACCESS_TOKEN_PREFFIX : "DRIVER_ACCESS_TOKEN_";
        try {
            if (CollectionUtils.isNotEmpty(headers.get(GatewayHeader.accessToken))) {
                String accessToken = headers.get(GatewayHeader.accessToken).get(0);
                RedisStringCacheService stringCacheService = SpringContextHolder.getBean(RedisStringCacheService.class);
                String jsonApiParam = stringCacheService.get(prefix + accessToken);
                if (StringUtils.isEmpty(jsonApiParam)) {
                    throw new Exception();
                }
                ApiParam apiParam = JsonUtils.readValue(jsonApiParam, ApiParam.class);
                BeanUtils.copyProperties(apiParam, o);
            }
            ReflectUtils.setFieldValue(o, GatewayHeader.appid, appid);
        } catch (Exception e) {
            log.warn("RequestBodyAdvice redis get apiParam error.");
            // 异常的时候做 jwt 降级方案
            if (CollectionUtils.isNotEmpty(headers.get(GatewayHeader.jwts))) {
                String jwts = headers.get(GatewayHeader.jwts).get(0);
                String parseJWT = JwtHelper.parseJWT(jwts);
                try {
                    ApiParam apiParam = JsonUtils.readValue(parseJWT, ApiParam.class);
                    BeanUtils.copyProperties(apiParam, o);
                } catch (IOException ex) {
                    log.error("RequestBodyAdvice afterBodyRead readValue error. parseJWT:{}", parseJWT, e);
                }
            }
        }

    }
}
